{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "直接用类名调用父类方法在使用单继承的时候没问题。但是如果使用多继承，会涉及到查找顺序（MRO）、重复调用（钻石继承）等种种问题，super 是用来解决多重继承问题的。\n",
    "\n",
    "# MRO\n",
    "\n",
    "MRO 就是类的方法解析顺序表, 其实也就是继承父类方法时的顺序表。\n",
    "\n",
    "MRO即method resolution order，用于判断子类调用的属性来自于哪个父类。**在Python2.3之前，MRO是基于深度优先算法的，自2.3开始使用C3算法，**定义类时需要继承object，这样的类称为**新式类**，否则为**旧式类**\n",
    "\n",
    "从图中可以看出，**旧式类查找属性时是深度优先搜索，新式类则是广度优先搜索**。\n",
    "\n",
    "![MRO](https://md-picture-1254350681.cos.ap-beijing.myqcloud.com/20210105151350.png)\n",
    "\n",
    "示例代码："
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 经典类（旧式类）\n",
    "\n",
    "考虑一种情形，B继承于A，C继承于A和B, 但C需要调用父类的\\_\\_init__()函数时，前者会导致父类A的\\_\\_init__()函数被调用2次，这是不希望看到的。而且子类要显式地指定父类，不符合DRY原则。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "A\n",
      "A\n",
      "B\n",
      "C\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<__main__.C at 0x25e8ca48470>"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "class A():\n",
    "    def __init__(self):\n",
    "        print('A')\n",
    "\n",
    "\n",
    "class B(A):\n",
    "    def __init__(self):\n",
    "        A.__init__(self)\n",
    "        print('B')\n",
    "\n",
    "\n",
    "class C(B, A):\n",
    "    def __init__(self):\n",
    "        A.__init__(self)\n",
    "        B.__init__(self)\n",
    "        print('C')\n",
    "\n",
    "\n",
    "C()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 新式类\n",
    "\n",
    "采用新式类，要求**最顶层的父类一定要继承于object**，这样就可以利用super()函数来调用父类的\\_\\_init__()等函数，每个父类都执行且执行一次，并不会出现重复调用的情况。而且在子类的实现中，不用到处写出所有的父类名字，符合DRY原则。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "A\n",
      "B\n",
      "C\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<__main__.C at 0x25e8caa81d0>"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "class A(object):\n",
    "    def __init__(self):\n",
    "        print('A')\n",
    "\n",
    "\n",
    "class B(A):\n",
    "    def __init__(self):\n",
    "        super(B, self).__init__()\n",
    "        print('B')\n",
    "\n",
    "\n",
    "class C(B, A):\n",
    "    def __init__(self):\n",
    "        super(C, self).__init__()\n",
    "        print('C')\n",
    "\n",
    "C()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "采用super()方式时，会自动找到第一个多继承中的第一个父类，但是如果还想强制调用其他父类的\\_\\_init__()函数或两个父类的同名函数时，就要用老办法了。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(<class '__main__.Person'>, <class '__main__.Consumer'>)\n",
      "[<class '__main__.Student'>, <class '__main__.Person'>, <class '__main__.Consumer'>, <class 'object'>]\n",
      "Person\n",
      "Consumer\n",
      "abc 90 F\n"
     ]
    }
   ],
   "source": [
    "class Person(object):\n",
    "    name = \"\"\n",
    "    sex = \"\"\n",
    "\n",
    "    def __init__(self, name, sex='U'):\n",
    "        print('Person')\n",
    "        self.name = name\n",
    "        self.sex = sex\n",
    "\n",
    "\n",
    "class Consumer(object):\n",
    "    def __init__(self):\n",
    "        print('Consumer')\n",
    "\n",
    "\n",
    "class Student(Person, Consumer):\n",
    "    def __init__(self, score, name):\n",
    "        print(Student.__bases__)\n",
    "        print(Student.mro())\n",
    "        super(Student, self).__init__(name, sex='F')\n",
    "        Consumer.__init__(self)\n",
    "        self.score = score\n",
    "\n",
    "\n",
    "s1 = Student(90, 'abc')\n",
    "print(s1.name, s1.score, s1.sex)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# C3 线性化算法\n",
    "\n",
    "C3算法最早被提出是用于Lisp的，应用在Python中是为了解决原来基于深度优先搜索算法不满足本地优先级，和单调性的问题。\n",
    "\n",
    "- 本地优先级：指声明时父类的顺序，比如C(A,B)，如果访问C类对象属性时，应该根据声明顺序，优先查找A类，然后再查找B类。\n",
    "\n",
    "- 单调性：如果在C的解析顺序中，A排在B的前面，那么在C的所有子类里，也必须满足这个顺序。\n",
    "\n",
    "通过C3算法，一个类的 MRO 应当如下公式计算得出：\n",
    "\n",
    "```\n",
    "L[object] = [object]\n",
    "L[C(B1…BN)] = [C] + merge(L[B1]…L[BN], [B1, … ,BN])\n",
    "```\n",
    "\n",
    "这里的关键在于 **merge** 步骤，其输入是一组列表，按照如下方式输出一个列表：\n",
    "\n",
    "1.  检查第一个列表的头元素（如 L[B1] 的头），记作 H。\n",
    "2.  若 H 未出现在其它列表的尾部，则将其输出，并将其从所有列表中删除，然后回到步骤 1；否则，取出下一个列表的头部记作 H，继续该步骤。\n",
    "3.  重复上述步骤，直至列表为空或者不能再找出可以输出的元素。如果是前一种情况，则算法结束；如果是后一种情况，说明无法构建继承关系，Python 会抛出异常。\n",
    "\n",
    "示例 1\n",
    "\n",
    "继承关系如下图  \n",
    "![](https://md-picture-1254350681.cos.ap-beijing.myqcloud.com/1249301-20190524235050394-577481731.png)\n",
    "\n",
    "根据上述 C3 算法的步骤来计算其 MRO\n",
    "\n",
    "首先计算 B1 的 MRO:  \n",
    "L[B1(A1,A2)] = [B1] + merge(L[A1], L(A2), [A1, A2])  \n",
    "             = [B1] + merge([A1,Obj], [A2,Obj], [A1,A2])  \n",
    "             = [B1, A1] + merge([Obj], [A2,Obj], [A2])  \n",
    "             = [B1, A1, A2] + merge([Obj], [Obj])  \n",
    "             = [B1, A1, A2, Obj]\n",
    "\n",
    "同理, 计算 B2 的 MRO(过程略):  \n",
    "L[B2(A3)] = [B2, A3, Obj]\n",
    "\n",
    "最终计算并得到 C 的 MRO  \n",
    "L[C(B1,B2)] = [C] + merge(L[B1(A1,A2)], L[B2(A3)], [B1,B2])  \n",
    "            = [C] + merge([B1, A1, A2, Obj], [B2, A3, Obj], [B1,B2])  \n",
    "            = [C, B1] + merge([A1, A2, Obj], [B2, A3, Obj], [B2])  \n",
    "            = [C, B1, A1] + merge([A2, Obj], [B2, A3, Obj], [B2])  \n",
    "            = [C, B1, A1, A2] + merge([Obj], [B2, A3, Obj], [B2])  \n",
    "            = [C, B1, A1, A2, B2] +merge([Obj], [A3, Obj])  \n",
    "            = [C, B1, A1, A2, B2, A3] +merge([Obj], [Obj])  \n",
    "            = [C, B1, A1, A2, B2, A3, Obj]\n",
    "\n",
    "根据 C3 算法成功构建了 MRO，所以这个类的继承关系是被允许的，而且根据 MRO 可以明确地指出应当如何去查找其父类的属性 / 方法。即按照 MRO 列表由前向后的顺序来查找。\n",
    "\n",
    "当然，我们完全没有必要去计算这个序列，直接使用. mro() 类方法即可查看该类的 MRO\n",
    "\n",
    "```python\n",
    "C.mro()\n",
    "\n",
    "[<class '__main__.C'>, <class '__main__.B1'>, <class '__main__.A1'>, <class '__main__.A2'>, <class '__main__.B2'>, <class '__main__.A3'>, <class 'object'>]\n",
    "\n",
    "\n",
    "\n",
    "```\n",
    "\n",
    "与我们计算的结果是相同的。\n",
    "\n",
    "**正确理解 MRO 是使用多重继承和 super() 完成多继承类协作任务的基础。**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# super函数用法\n",
    "\n",
    "理解了 python 的 MRO 之后，我们就可以更加准确地使用 `super()` 函数，以及使用 super() 完成**多继承协同任务**\n",
    "\n",
    "`super().methodxx()` 是调用父类中的方法，这个搜索顺序当然是按照 MRO 从前向后开始进行的\n",
    "\n",
    "`super([type][, object-or-type])`  \n",
    "根据官方文档，**`super`函数返回一个委托类`type`的父类或者兄弟类方法调用的代理对象**。`super`函数用来调用已经在子类中重写过的父类方法。\n",
    "\n",
    "这句话其实很难看明白，为什么除了父类还可能是兄弟类?\n",
    "\n",
    "要理解这句话，先谈谈`super`的参数的传入方式不同带来的不同之处\n",
    "\n",
    "常见的是直接调用`super()`, 这其实是`super(type, obj)`的简写方式，将当前的类传入`type`参数，同时将实例对象传入`type-or-object`参数，这两个实参必须确保`isinstance(obj, type)`为`True`。\n",
    "\n",
    "使用该方法调用的`super`函数返回的代理类是`obj`所属类的 MRO 中，排在 type 之后的下一个父类。\n",
    "\n",
    "示例:  \n",
    "类的继承结构如下  \n",
    "![](https://md-picture-1254350681.cos.ap-beijing.myqcloud.com/1249301-20190525170912709-1726985906.png)\n",
    "\n",
    "```py\n",
    "class A: pass\n",
    "class B: pass\n",
    "class C(A,B): pass\n",
    "```\n",
    "\n",
    "类 C 的 MRO 为 [C, A, B, object]\n",
    "\n",
    "现在我们为其添加一个方法`x()`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "run C.x\n",
      "run A.x\n",
      "run B.x\n",
      "<__main__.C object at 0x0000025E8CAF1438>\n",
      "<__main__.C object at 0x0000025E8CAF1438>\n",
      "<__main__.C object at 0x0000025E8CAF1438>\n"
     ]
    }
   ],
   "source": [
    "class A:\n",
    "    def x(self):\n",
    "        print('run A.x')\n",
    "        super().x()\n",
    "        print(self)\n",
    "\n",
    "\n",
    "class B:\n",
    "    def x(self):\n",
    "        print('run B.x')\n",
    "        print(self)\n",
    "\n",
    "\n",
    "class C(A, B):\n",
    "    def x(self):\n",
    "        print('run C.x')\n",
    "        super().x()\n",
    "        print(self)\n",
    "\n",
    "\n",
    "C().x()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "该方法最先出现是作为 C 的实例方法，根据 MRO，我们很清楚，下一步它会调用其 MRO 父类中的同名方法，即 A 中的 x() 方法，但是，我们在 A 的 x() 方法中再次使用了 super()，这时候会怎么样呢?\n",
    "\n",
    "\n",
    "在调用了 A 中的`x()`方法之后，下一个调用的是 B 中的`x()`方法，在继承结构中，类 A 和类 B 互为兄弟关系，`super()`在 A 中调用的时候，最终却调用其兄弟的同名方法，这就是之前说的，`super`函数返回一个委托类`type`的父类或者兄弟类方法调用的代理对象。\n",
    "\n",
    "那么，为什么?  \n",
    "根据 `print(self)` 的输出，所有在这些 super() 的调用过程中，self 参数传入的是同一个 obj，就是我们初始化的 C(), 在内存中id位置也是相同的。\n",
    "\n",
    "之前已经说过，`super()`是`super(type, obj)`的简写，在调用`super()`时，`type`参数传入的是当前的类，而`obj`参数则是默认传入当前的实例对象，在`super()`的后续调用中，`obj`一直未变，而实际传入的`class`是动态变化，不过，**在首次调用时，MRO 就已经被确定**，是`obj`所属类 (即 C) 的 MRO，因此`class`参数的作用就是从已确定的 MRO 中找到位于其后紧邻的类，作为再次调用`super()`时查找该方法的下一个类。  \n",
    "即，`super`函数这一部分的核心逻辑应该为\n",
    "\n",
    "```python\n",
    "def super(class, obj):\n",
    "    mro_list = obj.__class__.mro()\n",
    "    next_parent_class = mro_list[mro_list.index(class)+1]\n",
    "    return next_parent_class\n",
    "```\n",
    "\n",
    "这就是为什么必须保证`isinstance(obj, type)`为`True`的原因，如果不是，那么可能`type`就不存在于`obj.__class__`的 MRO 列表中，该算法就无法正确找到下一个应当被查找的类。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 多继承协同任务\n",
    "\n",
    "**如果我们在某个类的父类中按照其 MRO 顺序，每个父类都写一个同名方法，同时每个该方法中都继续调用`super()`，直到在 MRO 列表`object`之前的最后一个类的同名方法中不再调用`super()`，**那么在调用该方法时，会在各个父类中按照 MRO 列表的顺序依次被调用，这个过程中存在数据的传递，代表它们之间可以**共享**某些数据，这就实现了多继承协同工作。\n",
    "\n",
    "而这种工作方式，通过重写方法是根本无法实现的。\n",
    "\n",
    "使用实例:\n",
    "\n",
    "继承结构如下图  \n",
    "![](https://md-picture-1254350681.cos.ap-beijing.myqcloud.com/1249301-20190525173624849-2039140699.png)\n",
    "\n",
    "我们试图达到的目的如下:  \n",
    "一个类`Final`继承`Header`以获得属性`header`  \n",
    "同时我们通过混合其他类来快捷地修饰`header`属性，例如继承类`Mixin1`会为`header`属性 (其数据类型为列表) 追加数据`data1`, 而继承类`Minix2`则会为`header`属性的头部添加元素`data2`, 注意，因为这些操作并不冲突，这些行为都不该相互覆盖。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Minix1:\n",
    "    \"\"\"该混合类为header列表末尾添加data1\"\"\"\n",
    "    def get_header(self):\n",
    "        print('run Minix1.get_header')\n",
    "        ctx = super().get_header()\n",
    "        ctx.append('data1')\n",
    "        return ctx\n",
    "\n",
    "\n",
    "class Minix2:\n",
    "    \"\"\"该混合类为header列表头部添加data2\"\"\"\n",
    "    def get_header(self):\n",
    "        print('run Minix2.get_header')\n",
    "        ctx = super().get_header()\n",
    "        ctx.insert(0, 'data2')\n",
    "        return ctx\n",
    "\n",
    "\n",
    "class Header:\n",
    "    header = []\n",
    "\n",
    "    def get_header(self):\n",
    "        print('run Headers.get_header')\n",
    "        return self.header if self.header else []\n",
    "\n",
    "\n",
    "class Final(Minix1, Minix2, Header):\n",
    "    def get_header(self):\n",
    "        return super().get_header()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "当然，我们可以定义更多的混合类，并从中选取所需的类来快速得到想要的`header`属性, 在这个例子中，这两个混合类已经足够说明问题。\n",
    "\n",
    "我们现在使用类 C 的`get_header()`方法来得到其`header`属性"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[<class '__main__.Final'>, <class '__main__.Minix1'>, <class '__main__.Minix2'>, <class '__main__.Header'>, <class 'object'>]\n"
     ]
    }
   ],
   "source": [
    "print(Final.mro())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "run Minix1.get_header\n",
      "run Minix2.get_header\n",
      "run Headers.get_header\n"
     ]
    }
   ],
   "source": [
    "header = Final().get_header()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['data2', 'data1']\n"
     ]
    }
   ],
   "source": [
    "print(header)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "看来，运行得很成功，我们实现了多继承协同工作的目标，通过混合不同个类，来模块化地快速得到想要的`header`属性。  \n",
    "而这种工作方法，通过单纯的重写某个方法根本无法实现的，因为重写任何方法，它会在 MRO 列表中找到最优先 (也就是最靠前) 的拥有同名方法的类，然后调用该方法，并且终止检索，某项属性仅仅会被**一个**方法所影响。\n",
    "\n",
    "这个特性，在 Django 的 CBV 中有相当程度的应用。\n",
    "\n",
    "## 小结：\n",
    "\n",
    "1. super并不是一个函数，是一个类名，形如super(B, self)事实上调用了super类的初始化函数，产生了一个super对象；\n",
    "2. super类的初始化函数并没有做什么特殊的操作，只是简单记录了类类型和具体实例；\n",
    "3. super(B, self).func的调用并不是用于调用当前类的父类的func函数；\n",
    "4. Python的多继承类是通过mro的方式来保证各个父类的函数被逐一调用，而且保证每个父类函数只调用一次（如果每个类都使用super）；\n",
    "5. 混用super类和非绑定的函数是一个危险行为，这可能导致应该调用的父类函数没有调用或者一个父类函数被调用多次。\n",
    "\n",
    "相关文章或参考:  \n",
    "\n",
    "- [Python 的方法解析顺序 (MRO)](https://www.cnblogs.com/whatisfantasy/p/6046991.html)\n",
    "- [Python 进阶 - 继承中的 MRO 与 super](https://www.cnblogs.com/crazyrunning/p/7095014.html)  \n",
    "- [python 继承与多重继承](https://blog.csdn.net/weixin_34267123/article/details/89545060)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.5"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {
    "height": "calc(100% - 180px)",
    "left": "10px",
    "top": "150px",
    "width": "181.273px"
   },
   "toc_section_display": true,
   "toc_window_display": true
  },
  "varInspector": {
   "cols": {
    "lenName": 16,
    "lenType": 16,
    "lenVar": 40
   },
   "kernels_config": {
    "python": {
     "delete_cmd_postfix": "",
     "delete_cmd_prefix": "del ",
     "library": "var_list.py",
     "varRefreshCmd": "print(var_dic_list())"
    },
    "r": {
     "delete_cmd_postfix": ") ",
     "delete_cmd_prefix": "rm(",
     "library": "var_list.r",
     "varRefreshCmd": "cat(var_dic_list()) "
    }
   },
   "types_to_exclude": [
    "module",
    "function",
    "builtin_function_or_method",
    "instance",
    "_Feature"
   ],
   "window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
